#include "Camera.h"

#include "Flag.h"
#include "Defines.h"
#include "App.h"
#include <math.h>

#if defined(__APPLE__) || defined(MACOSX)
	   #include <OpenGL/OpenGL.h>
#endif
#if defined(_WIN32)
	#include <GL/gl.h>
#endif

namespace Y{

Camera::Camera():
	Entity				(NULL),
	mod					(1.0),
	translationMod		(1.0),
	rotationMod			(1.0),
	scalingMod			(0.01),
	smoothMod			(0.1),
	smoothCamera		(true),
	updating			(true),
	adjustedXY			(true),
	reverseY			(false),
	rotationModifierFlag(O_CTRL)
{
	scalings(1.0,1.0,1.0);
}

void Camera::handleConfig ( std::vector< ConfigComponent >& cv ){
	for ( int i = 0; i < cv.size(); ++i ){
		if ( cv[i].name == "rotation speed" ){
			rotationMod = cv[i].to<float>();	handled();
		}
		else if ( cv[i].name == "translation speed" ){
			translationMod = cv[i].to<float>();	handled();
		}
		else if ( cv[i].name == "scaling speed" ){
			scalingMod = cv[i].to<float>();		handled();
		}
		else if ( cv[i].name == "speed" ){
			mod = cv[i].to<float>();			handled();
		}
		else if ( cv[i].name == "translations" ){
			Vertex3f& v = cv[i].to<Vertex3f>();
			translations( v.x(), v.y(), v.z() );
			handled();
		}
	}
	
	Entity::handleConfig( cv );
}

bool Camera::keyPress (unsigned char c, int key_mod){
	
	Flag_16 modFlag(key_mod);
	
	Vertex3d* TRS_ptr;
	Vertex3b* active_ptr;
	double specificMod;
	if ( modFlag.includes(rotationModifierFlag) ){  TRS_ptr = &dRotations;specificMod = rotationMod;active_ptr = &dActive[1];  }
	else if ( modFlag.includes(O_ALT) ){  TRS_ptr = &dScalings;specificMod = scalingMod;active_ptr = &dActive[2];  }
	else{  TRS_ptr = &dTranslations;specificMod = translationMod;active_ptr = &dActive[0];  }
	Vertex3d& TRS = *TRS_ptr;
	Vertex3b& active = *active_ptr;
	
	if ( c == O_KEY_UP ){ c = '8'; } else if ( c == O_KEY_DOWN ){ c = 'k'; } 
	else if ( c == O_KEY_LEFT ){ if ( modFlag.includes(rotationModifierFlag) ){ c = 'y'; } else{ c = 'u'; } }
	else if ( c == O_KEY_RIGHT ){ if ( modFlag.includes(rotationModifierFlag) ){ c = 'p'; } else{ c = 'o'; } }
	
	if ( modFlag.includes(rotationModifierFlag) ){	if ( c == '8' ) { c = 'o'; } else if ( c == 'k' ) { c = 'u'; }
										else if ( c == 'o' ){ c = '8'; } else if ( c == 'u' ) { c = 'k'; }		}
										
	bool used = true;
	
	if ( c == '8' ){ TRS.y() = mod * specificMod;if ( reverseY ){ TRS.y() *= -1.0; }active[1] = true; }
	else if ( c == 'k' ){ TRS.y() = -1.0 * mod * specificMod;if ( reverseY ){ TRS.y() *= -1.0; }active[1] = true; }
	else if ( c == 'u' ){ TRS.x() = mod * specificMod;active[0] = true; }
	else if ( c == 'o'){ TRS.x() = -1.0 * mod * specificMod;active[0] = true; }
	else if ( c == 'y'){ TRS.z() = mod * specificMod;active[2] = true; }
	else if ( c == 'p'){ TRS.z() = -1.0 * mod * specificMod;active[2] = true; }
	
	else if ( c == 'x'){ TRS.z() = mod * specificMod;active[2] = true; }
	else if ( c == 'z'){ TRS.z() = -1.0 * mod * specificMod;active[2] = true; }
	else{ used = false; }
	
	return used;
}

bool Camera::keyRelease (unsigned char c, int key_mod){
	Flag_16 modFlag(key_mod);
	
	Vertex3d* TRS_ptr;
	Vertex3b* active_ptr;
	double specificMod;
	if ( modFlag.includes(rotationModifierFlag) ){  TRS_ptr = &dRotations;specificMod = rotationMod;active_ptr = &dActive[1];  }
	else if ( modFlag.includes(O_ALT) ){  TRS_ptr = &dScalings;specificMod = scalingMod;active_ptr = &dActive[2];  }
	else{  TRS_ptr = &dTranslations;specificMod = translationMod;active_ptr = &dActive[0];  }
	Vertex3b& active = *active_ptr;
	Vertex3d& TRS = *TRS_ptr;
	
	if ( c == O_KEY_UP ){ c = '8'; } else if ( c == O_KEY_DOWN ){ c = 'k'; } 
	else if ( c == O_KEY_LEFT ){ if ( modFlag.includes(rotationModifierFlag) ){ c = 'y'; } else{ c = 'u'; } }
	else if ( c == O_KEY_RIGHT ){ if ( modFlag.includes(rotationModifierFlag) ){ c = 'p'; } else{ c = 'o'; } }
	
	if ( modFlag.includes(rotationModifierFlag) ){	if ( c == '8' ) { c = 'o'; } else if ( c == 'k' ) { c = 'u'; }
										else if ( c == 'o' ){ c = '8'; } else if ( c == 'u' ) { c = 'k'; }		}
	
	bool used = true;
	
	if ( smoothCamera ){
		if ( c == '8' ){ active[1] = false; }
		else if ( c == 'k' ){ active[1] = false; }
		else if ( c == 'u' ){ active[0] = false; }
		else if ( c == 'o'){ active[0] = false; }
		else if ( c == 'y'){ active[2] = false; }
		else if ( c == 'p'){ active[2] = false; }
		
		else if ( c == 'x'){ active[2] = false; }
		else if ( c == 'z'){ active[2] = false; }		
		else{ used = false; }
	}
	else{
		if ( c == '8' ){ TRS.y() = 0.0; }
		else if ( c == 'k' ){ TRS.y() = 0.0; }
		else if ( c == 'u' ){ TRS.x() = 0.0; }
		else if ( c == 'o' ){ TRS.x() = 0.0; }
		else if ( c == 'y' ){ TRS.z() = 0.0; }
		else if ( c == 'p' ){ TRS.z() = 0.0; }
		
		else if ( c == 'x' ){ TRS.z() = 0.0; }
		else if ( c == 'z' ){ TRS.z() = 0.0; }
		else{ used = false; }
	}
	
	return used;
}

void Camera::timer (float){
	
	if ( ! updating ){ return; }
	
	Flag keyMods( App->getKeyModifiers() );
	float shiftMult = 1.0;
	
	if ( adjustedXY ){
		float rotationsZRad = (rotations.z() / 180.0) * 3.14159;
		translations.x() += dTranslations.x() * cos(rotationsZRad) * shiftMult;
		translations.y() -= dTranslations.x() * sin(rotationsZRad) * shiftMult;

		translations.x() += dTranslations.y() * sin(rotationsZRad) * shiftMult;
		translations.y() += dTranslations.y() * cos(rotationsZRad) * shiftMult;
	}
	else{
		translations.x() += dTranslations.x();
		translations.y() += dTranslations.y();
	}
	
	translations.z() += dTranslations.z() * shiftMult;
	rotations += dRotations * shiftMult;
	scalings += dScalings * shiftMult;
	
	if ( smoothCamera ){
		for ( int i = 0; i < 3; ++i ){
			if ( ! dActive[0][i] ){
//				dTranslations[i] = to_zero( dTranslations[i], mod * translationMod * smoothMod * shiftMult );
			}
			if ( ! dActive[1][i] ){
//				dRotations[i] = to_zero( dRotations[i], mod * rotationMod * smoothMod * shiftMult );
			}
			if ( ! dActive[2][i] ){
//				dScalings[i] = to_zero( dScalings[i], mod * (1.0 / scalingMod) * smoothMod * shiftMult );
			}
		}
	}
}

void Camera::apply (){
	glTranslatef( 0.0,0.0, translations.z() );
	glRotated( rotations.x(),1.0,0.0,0.0 );
	glRotated( rotations.y(),0.0,1.0,0.0 );
	glRotated( rotations.z(),0.0,0.0,1.0 );
	glTranslatef( translations.x(), translations.y(), 0.0 );
	glScaled( scalings.x(), scalings.y(), scalings.z() );
}

}